Tingkatkan pengembangan TypeScript Anda dengan menerapkan jenis kesalahan kustom. Pelajari cara membuat, melempar, dan menangkap kesalahan tertentu untuk debugging yang lebih jelas dan aplikasi yang lebih tangguh di seluruh dunia.
Menguasai Pesan Kesalahan TypeScript: Membuat Jenis Kesalahan Kustom untuk Aplikasi yang Kuat
Dalam dunia pengembangan perangkat lunak yang dinamis, penanganan kesalahan dengan baik sangat penting untuk membangun aplikasi yang tangguh dan mudah dipelihara. TypeScript, dengan sistem pengetikannya yang kuat, menawarkan fondasi yang kuat untuk menangkap banyak potensi masalah pada waktu kompilasi. Namun, kesalahan runtime adalah bagian tak terhindarkan dari aplikasi apa pun. Meskipun mekanisme penanganan kesalahan bawaan TypeScript kuat, ada kalanya kita membutuhkan manajemen kesalahan yang lebih spesifik dan sadar konteks. Di sinilah implementasi jenis kesalahan kustom menjadi alat yang sangat diperlukan bagi pengembang di seluruh dunia.
Panduan komprehensif ini akan mempelajari seluk-beluk pembuatan, pemanfaatan, dan pengelolaan jenis kesalahan kustom di TypeScript. Kita akan menjelajahi manfaat, strategi implementasi praktis, dan memberikan wawasan yang dapat ditindaklanjuti yang dapat diterapkan pada proyek skala apa pun, terlepas dari lokasi geografis atau ukuran tim.
Mengapa Jenis Kesalahan Kustom Penting dalam Pengembangan Global
Sebelum kita menyelami 'bagaimana', mari kita tetapkan 'mengapa'. Mengapa pengembang, terutama mereka yang bekerja dalam tim internasional atau melayani basis pengguna global, harus menginvestasikan waktu dalam jenis kesalahan kustom? Alasannya banyak:
- Kejelasan dan Keterbacaan yang Ditingkatkan: Pesan kesalahan generik bisa jadi samar dan tidak membantu. Jenis kesalahan kustom memungkinkan Anda memberikan pesan yang spesifik dan deskriptif yang dengan jelas menunjukkan sifat masalah, membuat debugging jauh lebih cepat, terutama untuk pengembang di zona waktu yang berbeda yang mungkin mengalami masalah untuk pertama kalinya.
- Efisiensi Debugging yang Ditingkatkan: Ketika kesalahan terjadi, mengetahui dengan tepat apa yang salah sangatlah penting. Jenis kesalahan kustom memungkinkan Anda mengkategorikan kesalahan, yang memungkinkan pengembang untuk dengan cepat menentukan sumber dan konteks kegagalan. Hal ini sangat berharga bagi tim terdistribusi di mana kolaborasi langsung mungkin terbatas.
- Penanganan Kesalahan Granular: Tidak semua kesalahan dibuat sama. Beberapa mungkin dapat dipulihkan, sementara yang lain mengindikasikan kegagalan kritis. Jenis kesalahan kustom memungkinkan Anda menerapkan blok tangkapan khusus untuk kategori kesalahan yang berbeda, yang memungkinkan strategi pemulihan kesalahan yang lebih terarah dan cerdas. Misalnya, kesalahan jaringan mungkin dapat dicoba kembali, sedangkan kegagalan otentikasi memerlukan alur pengguna yang berbeda.
- Informasi Khusus Domain: Aplikasi Anda kemungkinan beroperasi dalam domain tertentu (misalnya, e-commerce, keuangan, perawatan kesehatan). Jenis kesalahan kustom dapat merangkum data khusus domain, memberikan konteks yang lebih kaya. Misalnya,
InsufficientFundsErrordalam sistem pemrosesan pembayaran dapat membawa detail tentang jumlah yang diminta dan saldo yang tersedia. - Pengujian yang Disederhanakan: Saat menulis pengujian unit atau integrasi, memiliki jenis kesalahan yang didefinisikan dengan baik memudahkan untuk menegaskan hasil yang diharapkan. Anda dapat secara khusus menguji terjadinya kesalahan kustom tertentu, memastikan logika penanganan kesalahan Anda berfungsi sebagaimana mestinya.
- Desain API yang Lebih Baik: Untuk aplikasi yang mengekspos API, jenis kesalahan kustom menyediakan cara terstruktur dan dapat diprediksi untuk mengkomunikasikan kesalahan ke klien yang mengonsumsi. Hal ini mengarah pada integrasi yang lebih kuat dan pengalaman pengembang yang lebih baik bagi pengguna API di seluruh dunia.
- Mengurangi Utang Teknis: Penanganan kesalahan yang proaktif dan terstruktur dengan baik mencegah penumpukan masalah yang membingungkan dan sulit di-debug, yang pada akhirnya mengurangi utang teknis dan meningkatkan pemeliharaan basis kode dalam jangka panjang.
Memahami Fondasi Penanganan Kesalahan TypeScript
TypeScript memanfaatkan mekanisme penanganan kesalahan fundamental JavaScript, terutama menggunakan blok try...catch...finally dan objek Error. Objek Error standar di JavaScript memiliki beberapa properti utama:
message: Deskripsi kesalahan yang dapat dibaca manusia.name: Nama jenis kesalahan (misalnya, 'Error', 'TypeError').stack: String yang berisi tumpukan panggilan pada titik di mana kesalahan dilempar.
Saat Anda melempar kesalahan generik di TypeScript, tampilannya mungkin seperti ini:
function processData(data: any) {
if (!data || typeof data !== 'object') {
throw new Error('Invalid data provided. Expected an object.');
}
// ... process data
}
try {
processData(null);
} catch (error) {
console.error(error.message);
}
Meskipun ini berhasil, pesan kesalahan 'Invalid data provided. Expected an object.' cukup generik. Bagaimana jika ada beberapa jenis data yang tidak valid? Bagaimana jika kita perlu membedakan antara parameter yang hilang dan parameter yang salah bentuk?
Menerapkan Jenis Kesalahan Kustom Pertama Anda
Cara paling umum dan efektif untuk membuat jenis kesalahan kustom di TypeScript adalah dengan memperluas kelas Error bawaan. Hal ini memungkinkan kesalahan kustom Anda untuk mewarisi semua properti objek kesalahan standar sambil memungkinkan Anda menambahkan properti dan metode khusus Anda sendiri.
Kelas Kesalahan Kustom Dasar
Mari kita mulai dengan kesalahan kustom sederhana, katakanlah, ValidationError, untuk mewakili masalah dengan validasi data.
class ValidationError extends Error {
constructor(message: string) {
super(message); // Panggil konstruktor induk (Error)
this.name = 'ValidationError'; // Atur nama kesalahan
// Mempertahankan jejak tumpukan yang tepat untuk tempat kesalahan kita dilempar (hanya tersedia di V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ValidationError);
}
}
}
Penjelasan:
- Kami mendefinisikan kelas
ValidationErroryangmemperluas Error. constructormengambil stringmessage, yang diteruskan ke panggilansuper(). Ini menginisialisasi kelasErrordasar dengan pesan.- Kami secara eksplisit mengatur
this.name = 'ValidationError'. Ini adalah praktik yang baik karena menggantikan nama 'Error' default dan dengan jelas mengidentifikasi jenis kesalahan kustom kami. - Baris
Error.captureStackTrace(this, ValidationError)adalah optimasi khusus V8 (umum di lingkungan Node.js) yang membantu dalam menangkap jejak tumpukan yang benar, tidak termasuk panggilan konstruktor itu sendiri dari tumpukan. Ini opsional tetapi direkomendasikan untuk debugging yang lebih baik.
Melempar dan Menangkap Kesalahan Kustom
Sekarang, mari kita lihat bagaimana kita dapat melempar dan menangkap ValidationError ini.
function validateEmail(email: string): void {
if (!email || !email.includes('@')) {
throw new ValidationError('Invalid email format. Email must contain an "@" symbol.');
}
console.log('Email is valid.');
}
try {
validateEmail('test@example.com');
validateEmail('invalid-email');
} catch (error) {
if (error instanceof ValidationError) {
console.error(`Validation Error: ${error.message}`);
// Anda dapat melakukan tindakan khusus untuk kesalahan validasi di sini
} else {
// Tangani kesalahan tak terduga lainnya
console.error(`An unexpected error occurred: ${error.message}`);
}
}
Dalam blok catch, kami menggunakan instanceof ValidationError untuk secara khusus mengidentifikasi dan menangani kesalahan kustom kami. Hal ini memungkinkan untuk logika penanganan kesalahan yang berbeda.
Menambahkan Properti Khusus Domain ke Kesalahan Kustom
Kekuatan sebenarnya dari jenis kesalahan kustom berasal dari kemampuan mereka untuk membawa informasi tambahan yang spesifik konteksnya. Mari kita buat kesalahan yang lebih canggih untuk aplikasi e-commerce hipotetis, seperti InsufficientStockError.
interface Product {
id: string;
name: string;
stock: number;
}
class InsufficientStockError extends Error {
public readonly productId: string;
public readonly requestedQuantity: number;
public readonly availableStock: number;
constructor(product: Product, requestedQuantity: number) {
const message = `Insufficient stock for product "${product.name}" (ID: ${product.id}). Requested: ${requestedQuantity}, Available: ${product.stock}.`;
super(message);
this.name = 'InsufficientStockError';
this.productId = product.id;
this.requestedQuantity = requestedQuantity;
this.availableStock = product.stock;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, InsufficientStockError);
}
}
}
// --- Contoh Penggunaan ---
const productInStock: Product = {
id: 'p123',
name: 'Wireless Mouse',
stock: 5
};
function placeOrder(product: Product, quantity: number): void {
if (quantity > product.stock) {
throw new InsufficientStockError(product, quantity);
}
console.log(`Order placed successfully for ${quantity} of ${product.name}.`);
// ... perbarui stok, proses pembayaran, dll.
}
try {
placeOrder(productInStock, 3);
placeOrder(productInStock, 7); // Ini akan melempar InsufficientStockError
} catch (error) {
if (error instanceof InsufficientStockError) {
console.error(`Order failed: ${error.message}`);
console.error(`Details - Product ID: ${error.productId}, Requested: ${error.requestedQuantity}, Available: ${error.availableStock}`);
// Tindakan yang mungkin: Sarankan produk alternatif, beri tahu pengguna, log untuk manajemen inventaris.
} else {
console.error(`An unexpected error occurred during order placement: ${error.message}`);
}
}
Dalam contoh ini:
InsufficientStockErrormemiliki properti tambahan:productId,requestedQuantity, danavailableStock.- Properti ini diinisialisasi dalam konstruktor dan diteruskan bersama dengan kesalahan.
- Saat menangkap kesalahan, kita dapat mengakses properti ini untuk memberikan umpan balik yang lebih rinci atau memicu logika pemulihan tertentu. Untuk audiens global, informasi granular ini sangat penting bagi tim dukungan atau sistem otomatis untuk memahami dan menyelesaikan masalah secara efisien di berbagai wilayah.
Menyusun Hirarki Kesalahan Kustom Anda
Untuk aplikasi yang lebih besar, Anda mungkin merasa bermanfaat untuk membuat hirarki kesalahan kustom. Hal ini memungkinkan penanganan kesalahan yang lebih terorganisir dan berlapis.
Pertimbangkan skenario di mana Anda memiliki berbagai jenis kesalahan terkait API:
// Kesalahan API Dasar
class ApiError extends Error {
constructor(message: string) {
super(message);
this.name = 'ApiError';
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ApiError);
}
}
}
// Kesalahan API Khusus yang mewarisi dari ApiError
class NetworkError extends ApiError {
public readonly statusCode?: number;
constructor(message: string, statusCode?: number) {
super(message);
this.name = 'NetworkError';
this.statusCode = statusCode;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, NetworkError);
}
}
}
class AuthenticationError extends ApiError {
constructor(message: string = 'Authentication failed. Please check your credentials.') {
super(message);
this.name = 'AuthenticationError';
if (Error.captureStackTrace) {
Error.captureStackTrace(this, AuthenticationError);
}
}
}
class ResourceNotFoundError extends ApiError {
public readonly resourceId: string;
constructor(resourceId: string, message: string = `Resource with ID "${resourceId}" not found.`) {
super(message);
this.name = 'ResourceNotFoundError';
this.resourceId = resourceId;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ResourceNotFoundError);
}
}
}
// --- Contoh Penggunaan ---
async function fetchUserData(userId: string): Promise<any> {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
if (response.status === 401) {
throw new AuthenticationError();
} else if (response.status === 404) {
throw new ResourceNotFoundError(userId);
} else {
throw new NetworkError(`API request failed with status ${response.status}`, response.status);
}
}
return response.json();
}
try {
const user = await fetchUserData('user123');
console.log('User data:', user);
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Authentication Error:', error.message);
// Alihkan ke halaman login secara global.
} else if (error instanceof ResourceNotFoundError) {
console.error('Resource Not Found:', error.message);
// Beri tahu pengguna bahwa sumber daya yang diminta tidak tersedia.
} else if (error instanceof NetworkError) {
console.error(`Network Error: ${error.message} (Status: ${error.statusCode})`);
// Berpotensi mencoba kembali permintaan atau memberi tahu pengguna tentang masalah koneksi.
} else {
console.error('An unknown API error occurred:', error.message);
}
}
Dalam struktur hierarkis ini:
ApiErrorberfungsi sebagai dasar umum untuk semua masalah terkait API.NetworkError,AuthenticationError, danResourceNotFoundErrormewarisi dariApiError, yang memungkinkan penanganan khusus dari setiap jenis.- Blok tangkapan dapat terlebih dahulu memeriksa kesalahan yang paling spesifik (misalnya,
AuthenticationError) dan kemudian kembali ke yang lebih umum (misalnya,ApiError) jika diperlukan. Ini sangat penting untuk aplikasi internasional di mana berbagai wilayah mungkin memiliki stabilitas jaringan atau persyaratan peraturan yang berbeda yang memengaruhi otentikasi.
Praktik Terbaik untuk Menerapkan Jenis Kesalahan Kustom
Untuk memaksimalkan manfaat jenis kesalahan kustom, pertimbangkan praktik terbaik berikut:
- Bersikap Spesifik: Beri nama kelas kesalahan Anda dengan jelas dan deskriptif. Nama itu sendiri harus menyampaikan sifat kesalahan.
- Mewarisi dari
Error: Selalu perpanjang kelasErrorbawaan untuk memastikan kesalahan kustom Anda berperilaku seperti kesalahan JavaScript standar dan memiliki properti yang diperlukan sepertimessagedanstack. - Atur Properti
name: Secara eksplisit aturthis.nameke nama kelas kesalahan kustom Anda. Ini sangat penting untuk identifikasi saat runtime. - Sertakan Data yang Relevan: Tambahkan properti ke kesalahan kustom Anda yang memberikan konteks dan memfasilitasi debugging atau pemulihan. Pikirkan tentang informasi apa yang dibutuhkan pengembang atau sistem otomatis untuk memahami dan menyelesaikan masalah.
- Dokumentasikan Kesalahan Anda: Sama seperti kode Anda, jenis kesalahan kustom Anda harus didokumentasikan. Jelaskan apa arti setiap kesalahan, properti apa yang dibawanya, dan kapan itu mungkin dilempar. Ini sangat penting untuk tim yang tersebar di seluruh dunia.
- Melempar dan Menangkap yang Konsisten: Tetapkan konvensi di dalam tim Anda tentang bagaimana dan di mana kesalahan harus dilempar dan bagaimana mereka harus ditangkap dan ditangani. Konsistensi ini adalah kunci untuk pendekatan terpadu untuk manajemen kesalahan di lingkungan terdistribusi.
- Hindari Penggunaan Berlebihan: Meskipun kesalahan kustom sangat kuat, jangan membuat kesalahan untuk setiap ketidaknyamanan kecil. Gunakan mereka untuk kondisi kesalahan yang berbeda yang memerlukan penanganan khusus atau membawa informasi kontekstual yang signifikan.
- Pertimbangkan Kode Kesalahan: Untuk sistem yang membutuhkan identifikasi kesalahan terprogram di berbagai bahasa atau platform, pertimbangkan untuk menambahkan kode kesalahan numerik atau string ke jenis kesalahan kustom Anda. Ini dapat berguna untuk lokalisasi atau memetakan kesalahan ke artikel dukungan tertentu.
- Penanganan Kesalahan Terpusat: Dalam aplikasi yang lebih besar, pertimbangkan modul atau layanan penanganan kesalahan terpusat yang mencegat dan memproses kesalahan, memastikan pencatatan, pelaporan, dan potensi mekanisme umpan balik pengguna yang konsisten di berbagai bagian aplikasi. Ini adalah pola penting untuk aplikasi global.
Pertimbangan Global dan Lokalisasi
Saat mengembangkan untuk audiens global, pesan kesalahan itu sendiri (properti message) perlu pertimbangan yang cermat:
- Hindari Lokalisasi di String Pesan Kesalahan Secara Langsung: Alih-alih mengkodekan pesan yang dilokalisasi di kelas kesalahan Anda, rancang sistem Anda untuk mengambil pesan yang dilokalisasi berdasarkan pengaturan lokal pengguna atau aplikasi. Kesalahan kustom Anda mungkin membawa
errorCodeataukeyyang dapat digunakan layanan lokalisasi. - Fokus pada Pesan yang Menghadap Pengembang: Audiens utama untuk pesan kesalahan terperinci di dalam objek kesalahan itu sendiri biasanya adalah pengembang. Oleh karena itu, pastikan pesan ini jelas, ringkas, dan akurat secara teknis. Pesan kesalahan yang menghadap pengguna harus ditangani secara terpisah dan mudah digunakan serta dilokalisasi.
- Karakter Set Internasional: Pastikan bahwa setiap properti string dalam kesalahan kustom Anda dapat menangani karakter set internasional dengan benar. Penanganan string standar TypeScript dan JavaScript umumnya mendukung Unicode dengan baik.
Misalnya, kesalahan kustom mungkin terlihat seperti ini:
class UserNotFoundError extends Error {
public readonly userId: string;
public readonly errorCode: string = 'ERR_USER_NOT_FOUND'; // Untuk lokalisasi/pencarian
constructor(userId: string, message: string = 'User not found.') {
super(message); // Pesan default, dapat ditimpa atau dicari.
this.name = 'UserNotFoundError';
this.userId = userId;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, UserNotFoundError);
}
}
}
// Dalam layanan lokalisasi:
function getLocalizedErrorMessage(error: Error & { errorCode?: string }, locale: string): string {
if (!error.errorCode) {
return error.message;
}
const messages: { [key: string]: { [key: string]: string } } = {
'en-US': {
'ERR_USER_NOT_FOUND': `User with ID ${ (error as any).userId } could not be found.`
},
'es-ES': {
'ERR_USER_NOT_FOUND': `No se encontró al usuario con ID ${ (error as any).userId }.`
}
// ... lokal lainnya
};
return messages[locale]?.[error.errorCode] || error.message;
}
// Penggunaan:
try {
// ... upaya untuk menemukan pengguna
throw new UserNotFoundError('abc-123');
} catch (error) {
if (error instanceof UserNotFoundError) {
const userMessage = getLocalizedErrorMessage(error, 'es-ES');
console.error(`Error: ${userMessage}`); // Menampilkan pesan Spanyol
} else {
console.error(`Generic error: ${error.message}`);
}
}
Kesimpulan
Menerapkan jenis kesalahan kustom di TypeScript bukan hanya masalah praktik pengkodean yang baik; ini adalah keputusan strategis yang secara signifikan meningkatkan ketahanan, pemeliharaan, dan pengalaman pengembang aplikasi Anda, terutama dalam konteks global. Dengan memperluas kelas Error, Anda dapat membuat objek kesalahan yang spesifik, informatif, dan dapat ditindaklanjuti yang merampingkan debugging, memungkinkan kontrol terperinci atas penanganan kesalahan, dan memberikan konteks khusus domain yang berharga.
Saat Anda terus membangun aplikasi canggih yang melayani audiens internasional yang beragam, berinvestasi dalam strategi kesalahan kustom yang terdefinisi dengan baik akan membuahkan hasil. Hal ini mengarah pada komunikasi yang lebih jelas di dalam tim pengembangan, resolusi masalah yang lebih efisien, dan, pada akhirnya, perangkat lunak yang lebih andal bagi pengguna di seluruh dunia. Rangkul kekuatan kesalahan kustom dan tingkatkan pengembangan TypeScript Anda ke tingkat berikutnya.